#define _GNU_SOURCE 1
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <poll.h>
using namespace std;

const int BUFFER_SIZE = 64;

int main(int argc, char* argv[])
{
    if (argc <= 2) {
        cerr << "Usage: " << argv[0] << " <IP> <Port>" << endl;
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in serverAddress;
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &serverAddress.sin_addr);
    serverAddress.sin_port = htons(port);

    int sockFd = socket(AF_INET, SOCK_STREAM, 0);
    assert(sockFd >= 0);

    if( connect(sockFd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {
        cout << "connection failed" << endl;
        close(sockFd);
        return 1;
    }

    pollfd fds[2];
    fds[0].fd = 0; // 标准输入
    fds[0].events = POLLIN; // 监听输入事件
    fds[0].revents = 0;
    fds[1].fd = sockFd; // 套接字
    fds[1].events = POLLIN | POLLRDHUP; // 监听输入和关闭事件
    fds[1].revents = 0;

    char readBuffer[BUFFER_SIZE];
    int pipeFd[2];
    int ret = pipe(pipeFd);
    assert(ret != -1);

    while (1) {
        ret = poll(fds, 2, -1); // 阻塞等待事件
        if (ret < 0) {
            cerr << "poll failure " << endl;
            break;
        }

        if (fds[1].revents & POLLRDHUP) { // 套接字关闭事件
            cout << "server close the connection" << endl;
            break;
        }
        else if (fds[1].revents & POLLIN) { // 套接字有数据
            memset(readBuffer, 0, BUFFER_SIZE);
            ret = recv(sockFd, readBuffer, BUFFER_SIZE - 1, 0); // 从套接字读取数据
            cout << readBuffer << endl;
        }

        if (fds[0].revents & POLLIN) { // 标准输入有数据，通过管道从内核发送数据
            ret = splice(0, nullptr, pipeFd[1], nullptr,    
                        32768, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);  // 从标准输入读取数据到管道
            ret = splice(pipeFd[0], nullptr, sockFd, nullptr, 
                        32768, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);  // 从管道写入数据到套接字
        }
    }

    close(sockFd);
    close(pipeFd[0]);
    close(pipeFd[1]);
    return 0;
}